package com.cooper.util;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpUtils {
	
	private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

	public static int CONNECT_TIMEOUT = 10000;
	public static int READ_TIMEOUT = 60000;
	public static final String USER_AGENT_DEFAULT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.58 Safari/537.36s";

	public static String doGet(String link) throws IOException {
		return doGet(link,StringUtils.CHARSET_UTF8, null);
	}
	
	public static String doGet(String link,String charset) throws IOException {
		return doGet(link, charset,null);
	}

	public static String doGet(String link, Map<String, String> headers) throws IOException {
		return doGet(link, StringUtils.CHARSET_UTF8, headers);
	}

	public static String doGet(String link, String charset, Map<String, String> headers) throws IOException {
		try {
			URL url = new URL(link);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");
			conn.setRequestProperty("User-Agent", USER_AGENT_DEFAULT);
			if (null != headers && !headers.isEmpty()) {
				for (Iterator<String> iter = headers.keySet().iterator(); iter.hasNext();) {
					String key = iter.next();
					conn.setRequestProperty(key, headers.get(key));
				}
			}
			return new String(IOUtils.toByteArray(conn.getInputStream()), charset);
		} catch (IOException e) {
			throw e;
		}
	}
	
	public static String httpsGet(String strUrl, String charset, Map<String, String> headers) throws Exception {
		try {
			URL url = new URL(strUrl);
			final TrustManager[] tm = {new X509TrustManager(){
				@Override
				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}
				@Override
				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				}
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				}
			};
			final SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			SSLSocketFactory ssf = sslContext.getSocketFactory();
			
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setHostnameVerifier(new HostnameVerifier(){
				@Override
				public boolean verify(String hostname, SSLSession session) {
					return true;
				}
	        });
			
			conn.setSSLSocketFactory(ssf);
			conn.setRequestMethod("GET");
			conn.setRequestProperty("User-Agent", USER_AGENT_DEFAULT);
			if (null != headers && !headers.isEmpty()) {
				for (Iterator<String> iter = headers.keySet().iterator(); iter.hasNext();) {
					String key = iter.next();
					conn.setRequestProperty(key, headers.get(key));
				}
			}
			return new String(IOUtils.toByteArray(conn.getInputStream()), charset);
		} catch (IOException e) {
			throw e;
		}
	}
	
	public static String doPost(String reqUrl, Map<String, String> paramMap, String charset) throws Exception {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost postMethod = new HttpPost(reqUrl);
		List<NameValuePair> nvpl = new ArrayList<NameValuePair>();
		
		postMethod.addHeader("User-Agent", USER_AGENT_DEFAULT);
		if(null != paramMap){
			for(String key: paramMap.keySet()){
				nvpl.add(new BasicNameValuePair(key, paramMap.get(key)));
			}
		}
		
		CloseableHttpResponse httpResponse = null;
		try {
			postMethod.setEntity(new UrlEncodedFormEntity(nvpl));
			httpResponse = httpClient.execute(postMethod);
			if(HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode() 
					|| HttpStatus.SC_NOT_MODIFIED == httpResponse.getStatusLine().getStatusCode()){
				HttpEntity entity = httpResponse.getEntity();
				return EntityUtils.toString(entity, charset);
			}
			throw new Exception("http post error, status code is " + httpResponse.getStatusLine().getStatusCode());
		} catch(Exception e){
			logger.error("post fail", e);
			throw e;
		} finally{
			try {
				if(null != httpResponse){
					httpResponse.close();
					httpResponse = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static String doUploadFile(String reqUrl, Map<String, String> parameters, String fileParamName,
			String filename, String contentType, byte[] data) {
		HttpURLConnection urlConn = null;
		try {
			urlConn = sendFormdata(reqUrl, parameters, fileParamName, filename, contentType, data);
			String responseContent = new String(IOUtils.toByteArray(urlConn.getInputStream()));
			return responseContent.trim();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			if (urlConn != null) {
				urlConn.disconnect();
			}
		}
	}

	private static HttpURLConnection sendFormdata(String reqUrl, Map<String, String> parameters, String fileParamName,
			String filename, String contentType, byte[] data) {
		HttpURLConnection urlConn = null;
		try {
			URL url = new URL(reqUrl);
			urlConn = (HttpURLConnection) url.openConnection();
			urlConn.setRequestMethod("POST");
			urlConn.setConnectTimeout(CONNECT_TIMEOUT);// （单位：毫秒）jdk
			urlConn.setReadTimeout(READ_TIMEOUT);// （单位：毫秒）jdk
																// 1.5换成这个,读操作超时
			urlConn.setDoOutput(true);

			urlConn.setRequestProperty("connection", "keep-alive");

			String boundary = "-----------------------------114975832116442893661388290519"; // 分隔符
			urlConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

			boundary = "--" + boundary;
			StringBuffer params = new StringBuffer();
			if (parameters != null) {
				for (Iterator<String> iter = parameters.keySet().iterator(); iter.hasNext();) {
					String name = iter.next();
					String value = parameters.get(name);
					params.append(boundary + "\r\n");
					params.append("Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n");
					// params.append(URLEncoder.encode(value,
					// StringUtils.CHARSET_UTF8));
					params.append(value);
					params.append("\r\n");
				}
			}

			StringBuilder sb = new StringBuilder();
			// sb.append("\r\n");
			sb.append(boundary);
			sb.append("\r\n");
			sb.append("Content-Disposition: form-data; name=\"" + fileParamName + "\"; filename=\"" + filename
					+ "\"\r\n");
			sb.append("Content-Type: " + contentType + "\r\n\r\n");
			byte[] fileDiv = sb.toString().getBytes();
			byte[] endData = ("\r\n" + boundary + "--\r\n").getBytes();
			byte[] ps = params.toString().getBytes();

			OutputStream os = urlConn.getOutputStream();
			os.write(ps);
			os.write(fileDiv);
			os.write(data);
			os.write(endData);

			os.flush();
			os.close();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return urlConn;
	}

	public static String post(String reqUrl, byte[] body, Charset charset, ContentType contentType, Map<String, String> headers) {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost postMethod = new HttpPost(reqUrl);
		postMethod.setEntity(new ByteArrayEntity(body, contentType));
		
		postMethod.addHeader("User-Agent", USER_AGENT_DEFAULT);
		if(null != headers && !headers.isEmpty()){
			for(String key: headers.keySet()){
				postMethod.addHeader(key, headers.get(key));
			}
		}
		
		CloseableHttpResponse httpResponse = null;
		try {
			httpResponse = httpClient.execute(postMethod);
			if(HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode() 
					|| HttpStatus.SC_NOT_MODIFIED == httpResponse.getStatusLine().getStatusCode()){
				
			} else {
				logger.error("http post error, status code is " + httpResponse.getStatusLine().getStatusCode());
			}
			
			HttpEntity entity = httpResponse.getEntity();
			if(null == charset){
				return EntityUtils.toString(entity);
			} else {
				return EntityUtils.toString(entity, charset);
			}
		} catch(Exception e){
			logger.error("post fail", e);
			throw new RuntimeException(e);
		} finally{
			try {
				if(null != httpResponse){
					httpResponse.close();
					httpResponse = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static String post(String reqUrl, byte[] body, Charset charset, ContentType contentType) {
		return post(reqUrl, body, charset, contentType, null);
	}
	
	public static String post(String reqUrl, byte[] body, Charset charset) {
		return post(reqUrl, body, charset, null);
	}
	
	
	  public static String post(String url, String json)
	    {
	        HttpClient client = HttpClients.createDefault();
	        HttpPost post = new HttpPost(url);
	        try
	        {
	            StringEntity s = new StringEntity(json,"UTF-8");
	            s.setContentEncoding("UTF-8");
	            s.setContentType("application/json");
	            post.setEntity(s);
	 
	            HttpResponse res = client.execute(post);
	            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
	            {
	                HttpEntity entity = res.getEntity();
	                return EntityUtils.toString(entity, "utf-8");
	            }
	        }
	        catch (Exception e)
	        {
	            throw new RuntimeException(e);
	        }
	        return "";
	    }
	
	public static String assembleUrl(String baseUrl, Map<String, String> args, String encoding) {
		StringBuilder url = new StringBuilder(baseUrl);
		if(!baseUrl.endsWith("?")){
			url.append("?");
		}
		int index = 0;
		for (Entry<String, String> entry : args.entrySet()) {
			String v = encode(entry.getValue(), encoding);
			url.append(entry.getKey()).append('=').append(v);
			if (index < args.size() - 1) {
				url.append("&");
			}
			index++;
		}
		return url.toString();
	}
	
	public static String encode(String value, String encoding) {
		if(StringUtils.isBlank(value)){
			return "";
		}
		try {
			return URLEncoder.encode(value, encoding);
		} catch (UnsupportedEncodingException e) {
			logger.error("encode fail,value=" + value + ",encoding=" + encoding, e);
			return value;
		}
	}
	
	public static String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || HttpUtils.isInnerIP(ip)) {
			ip = request.getHeader("X-Real-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || HttpUtils.isInnerIP(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
	
	private static long getIpNum(String ipAddress) {
		String[] ip = ipAddress.split("\\.");
		long a = Integer.parseInt(ip[0]);
		long b = Integer.parseInt(ip[1]);
		long c = Integer.parseInt(ip[2]);
		long d = Integer.parseInt(ip[3]);

		long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
		return ipNum;
	}
	
	public static boolean isInnerIP(String ipAddress) {
		boolean isInnerIp = false;
		try{
			
			long ipNum = getIpNum(ipAddress);
			/**
			 * 私有IP：A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类
			 * 192.168.0.0-192.168.255.255 当然，还有127这个网段是环回地址
			 **/
			long aBegin = getIpNum("10.0.0.0");
			long aEnd = getIpNum("10.255.255.255");
			long bBegin = getIpNum("172.16.0.0");
			long bEnd = getIpNum("172.31.255.255");
			long cBegin = getIpNum("192.168.0.0");
			long cEnd = getIpNum("192.168.255.255");
			isInnerIp = isInner(ipNum, aBegin, aEnd)
					|| isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
					|| ipAddress.equals("127.0.0.1");
		}catch(Exception e){}
		return isInnerIp;
	}
	
	private static boolean isInner(long userIp, long begin, long end) {
		return (userIp >= begin) && (userIp <= end);
	}
}
